package net.avcompris.util.json;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.json.simple.JSONObject;

import com.avcompris.util.AbstractUtils;

/**
 * utility class for JSON manipulation.
 * 
 * @author David Andrianavalontsalama Copyright Avantage Compris SARL 2013 ©
 */
public abstract class JsonUtils extends AbstractUtils {

	/**
	 * retrieve a property from a JSON map.
	 * 
	 * @param <T>
	 *            the class that represents the <tt>type</tt> passed as
	 *            parameter
	 * @param type
	 *            the required type
	 * @param json
	 *            the JSON object (a map)
	 * @param propertyName
	 *            the property's name
	 * @return the property's value
	 */
	public static <T> T getProperty(final Class<T> type, final JSONObject json,
			final String propertyName) {

		nonNullArgument(type, "type");
		nonNullArgument(json, "json");
		nonNullArgument(propertyName, "propertyName");

		final Map<?, ?> map = json;

		final Object value = map.get(propertyName);

		if (value == null) {
			return null;
		}

		if (!type.isInstance(value)) {

			if (type.equals(String.class)) {
				return type.cast(value.toString());
			}

			throw new ClassCastException("Property value for \"" + propertyName
					+ "\" is not an instance of " + type.getName() + ": "
					+ value.getClass().getName());
		}

		return type.cast(value);
	}

	/**
	 * retrieve the property names from a JSON map.
	 * 
	 * @param json
	 *            the JSON object (a map)
	 * @return the property names
	 */
	public static String[] getPropertyNames(final JSONObject json) {

		nonNullArgument(json, "json");

		final Map<?, ?> map = json;

		final String[] names = new String[map.size()];

		int i = 0;

		for (final Object key : map.keySet()) {

			if (!(key instanceof String)) {
				throw new ClassCastException("Property name #" + i
						+ " is not an instance of String: " + key);
			}

			names[i] = (String) key;

			++i;
		}

		return names;
	}

	/**
	 * retrieve a {@link String} property from a JSON map.
	 * 
	 * @param json
	 *            the JSON object (a map)
	 * @param propertyName
	 *            the property's name
	 * @return the property's value
	 */
	public static String getStringProperty(final JSONObject json,
			final String propertyName) {

		return getProperty(String.class, json, propertyName);
	}

	/**
	 * retrieve a {@link Boolean} property from a JSON map.
	 * 
	 * @param json
	 *            the JSON object (a map)
	 * @param propertyName
	 *            the property's name
	 * @return the property's value
	 */
	public static Boolean getBooleanProperty(final JSONObject json,
			final String propertyName) {

		return getProperty(Boolean.class, json, propertyName);
	}

	/**
	 * retrieve a {@link List} property from a JSON map.
	 * 
	 * @param json
	 *            the JSON object (a map)
	 * @param propertyName
	 *            the property's name
	 * @return the property's value
	 */
	public static Collection<?> getListProperty(final JSONObject json,
			final String propertyName) {

		return getProperty(List.class, json, propertyName);
	}

	/**
	 * retrieve a {@link Object} property from a JSON map.
	 * 
	 * @param json
	 *            the JSON object (a map)
	 * @param propertyName
	 *            the property's name
	 * @return the property's value
	 */
	public static Object getObjectProperty(final JSONObject json,
			final String propertyName) {

		return getProperty(Object.class, json, propertyName);
	}

	/**
	 * retrieve the map with a unique entry of a YAML map.
	 * 
	 * @param yaml
	 *            the YAML object (must be a map)
	 * @return the same YAML object, but verified and cast as a map
	 */
	/*
	 * private static Map<?, ?> getUniqueEntryMap(final Object yaml) {
	 * 
	 * nonNullArgument(yaml, "yaml");
	 * 
	 * if (!(yaml instanceof Map<?, ?>)) { throw new IllegalArgumentException(
	 * "yaml should be an instance of Map<?, ?>: " + yaml.getClass().getName());
	 * }
	 * 
	 * final Map<?, ?> map = (Map<?, ?>) yaml;
	 * 
	 * if (map.size() != 1) { throw new IllegalArgumentException(
	 * "yaml should be a Map<?, ?> of size 1, but was: " + map.size()); } return
	 * map; }
	 * 
	 * /** retrieve the unique value of a YAML map.
	 * 
	 * @param yaml the YAML object (must be a map)
	 * 
	 * @return the value
	 */
	/*
	 * public static Object getValue(final Object yaml) {
	 * 
	 * final Map<?, ?> map = getUniqueEntryMap(yaml);
	 * 
	 * return map.values().iterator().next(); }
	 */
}
